package pl;

import javacard.framework.Util;
import javacard.security.MessageDigest;
import javacard.security.Signature;

public class KeyEvolutionEngine {

	/* input data */
	byte[] key;
	byte[] preS;
	byte[] random;

	/* output data */
	byte[] nextKey;
	byte[] resultHmac;

	private byte[] C;
	byte[] shared;
	short sharLen;
	boolean finalized;

	HMAC hmac;
	KDF2BytesGenerator kdf2;
	MessageDigest md;

	public static final byte ALG_HMAC_SHA1 = 0x01;
	public static final byte ALG_HMAC_SHA_256 = 0x02;
	public static final byte ALG_HMAC_SHA_384 = 0x03;
	public static final byte ALG_HMAC_SHA_512 = 0x04;

	public KeyEvolutionEngine(byte messageDigest) {

		switch (messageDigest) {

		case ALG_HMAC_SHA1:
			md = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
			break;
		case ALG_HMAC_SHA_256:
			md = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
			break;
		case ALG_HMAC_SHA_384:
			md = MessageDigest.getInstance(MessageDigest.ALG_SHA_384, false);
			break;
		case ALG_HMAC_SHA_512:
			md = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
			break;
		default:
			md = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
			break;
		}

		hmac = new HMAC(md);
		kdf2 = new KDF2BytesGenerator(md);
		
		resultHmac = new byte[hmac.size];
		C = new byte[4];
		shared = new byte[128];
	}

	public void init(byte[] key) {
		this.key = key;
		this.nextKey = key;
	}

	public void FollowKeyEvo(byte[] preS, byte[] random) {
		
		sharLen = 0;
		Util.arrayCopy(key, (short) 0, shared, sharLen, (short) key.length);
		sharLen += key.length;
		Util.arrayCopy(preS, (short) 0, shared, sharLen, (short) preS.length);
		sharLen += preS.length;
		Util.arrayCopy(random, (short) 0, shared, sharLen,
				(short) random.length);
		sharLen += random.length;

		kdf2.init(shared);
		kdf2.generateBytes(this.nextKey, (short) 0, (short) this.nextKey.length);

		//generation of new key
		if (this.nextKey.length < this.md.getLength()) {
			this.nextKey = new byte[(short) (this.md.getLength() + 1)];
		}
		this.random = random;
		finalized = false;
	}

	public boolean verifyHmac(byte[] message, byte[] hMac) {

		hmac.reset();
		hmac.init(this.nextKey);
		hmac.update(message);
		hmac.digest(resultHmac);

		if (hMac == resultHmac)
			return true;
		/*
		 * try { //verification always using modified Key
		 * this.HmacEngine.init(new SecretKeySpec(this.NextKey,
		 * this.HmacAlgorithm)); byte[] result =
		 * this.HmacEngine.doFinal(message); if (Arrays.equals(hmac, result)) {
		 * //verification ok return true; }
		 * 
		 * } catch (InvalidKeyException ex) {
		 * Logger.getLogger(KeyEvolutionEngine
		 * .class.getName()).log(Level.SEVERE, null, ex); }
		 */
		return false;
	}

	private byte[] createHmac(byte[] message, byte[] key) {

		hmac.reset();
		hmac.init(key);
		hmac.update(message);
		hmac.digest(resultHmac);
		return resultHmac;

		/*
		 * try { this.HmacEngine.init(new SecretKeySpec(key,
		 * this.HmacAlgorithm)); byte[] result =
		 * this.HmacEngine.doFinal(message); return result;
		 * 
		 * } catch (InvalidKeyException ex) {
		 * Logger.getLogger(KeyEvolutionEngine
		 * .class.getName()).log(Level.SEVERE, null, ex); }
		 */

	}

	public byte[] createHmacWithNewKey(byte[] message) {
		return this.createHmac(message, this.nextKey);
	}

	public byte[] createHmacWithOldKey(byte[] message) {
		return this.createHmac(message, this.key);
	}

	public byte[] getNextKey() {
		return this.nextKey;
	}
}
